package app

import (
	"net/http"
)

var (
	methods = [...]string{
		http.MethodGet,
		http.MethodPost,
		http.MethodDelete,
		http.MethodPut,
		http.MethodConnect,
		http.MethodHead,
		http.MethodOptions,
		http.MethodPatch,
		http.MethodTrace,
	}
)

// Find 路由查找匹配到符合条件的url
func (r *Router) Find(method, path string, ctx *Context) (httpHandler HandlerFunc, httpMiddleware []MiddleWare) {
	ctx.path = path
	cn := r.tree // Current node as root

	var (
		search  = path
		child   *node  // Child node
		n       int    // Param counter
		nk      kind   // Next kind
		nn      *node  // Next node
		ns      string // Next search
		pvalues = ctx.pvalues
	)

	// Search order static > param > any
	for {
		if search == "" {
			break
		}

		pl := 0 // Prefix length
		l := 0  // LCP length

		if cn.label != ':' {
			sl := len(search)
			pl = len(cn.prefix)

			// LCP
			max := pl
			if sl < max {
				max = sl
			}
			for ; l < max && search[l] == cn.prefix[l]; l++ {
			}
		}

		if l == pl {
			// Continue search
			search = search[l:]
		} else {
			cn = nn
			search = ns
			if nk == pkind {
				goto Param
			} else if nk == akind {
				goto Any
			}
			// Not found
			return
		}

		if search == "" {
			break
		}

		// Static node
		if child = cn.findChild(search[0], skind); child != nil {
			// Save next
			if cn.prefix[len(cn.prefix)-1] == '/' {
				nk = pkind
				nn = cn
				ns = search
			}
			cn = child
			continue
		}

		// Param node
	Param:
		if child = cn.findChildByKind(pkind); child != nil {
			if len(pvalues) == n {
				continue
			}

			// Save next
			if cn.prefix[len(cn.prefix)-1] == '/' {
				nk = akind
				nn = cn
				ns = search
			}

			cn = child
			i, l := 0, len(search)
			for ; i < l && search[i] != '/'; i++ {
			}
			pvalues[n] = search[:i]
			n++
			search = search[i:]
			continue
		}

		// Any node
	Any:
		if cn = cn.findChildByKind(akind); cn == nil {
			if nn != nil {
				cn = nn
				nn = cn.parent
				search = ns
				if nk == pkind {
					goto Param
				} else if nk == akind {
					goto Any
				}
			}
			// Not found
			return
		}
		pvalues[len(cn.pnames)-1] = search
		break
	}

	httpHandler = cn.findHandler(method)
	ctx.path = cn.ppath
	ctx.pnames = cn.pnames
	if m, ok := routers.middlewareMap[cn.ppath]; ok == true {
		httpMiddleware = m
	}

	if httpHandler == nil {
		httpHandler = cn.checkMethodNotAllowed()
		if cn = cn.findChildByKind(akind); cn == nil {
			return
		}
		if h := cn.findHandler(method); h != nil {
			httpHandler = h
		} else {
			httpHandler = cn.checkMethodNotAllowed()
		}
		ctx.path = cn.ppath
		ctx.pnames = cn.pnames
		pvalues[len(cn.pnames)-1] = ""
	}
	ctx.pvalues = pvalues

	return
}

func (n *node) findHandler(method string) (httpHandler HandlerFunc) {
	switch method {
	case http.MethodConnect:
		httpHandler = n.methodHandler.connect
	case http.MethodDelete:
		httpHandler = n.methodHandler.delete
	case http.MethodGet:
		httpHandler = n.methodHandler.get
	case http.MethodHead:
		httpHandler = n.methodHandler.head
	case http.MethodOptions:
		httpHandler = n.methodHandler.options
	case http.MethodPatch:
		httpHandler = n.methodHandler.patch
	case http.MethodPost:
		httpHandler = n.methodHandler.post
	case http.MethodPut:
		httpHandler = n.methodHandler.put
	case http.MethodTrace:
		httpHandler = n.methodHandler.trace
	default:
	}
	if n.methodHandler.any != nil {
		httpHandler = n.methodHandler.any
	}
	return httpHandler
}

func (n *node) findChild(l byte, t kind) *node {
	for _, c := range n.children {
		if c.label == l && c.kind == t {
			return c
		}
	}
	return nil
}

func (n *node) findChildByKind(t kind) *node {
	for _, c := range n.children {
		if c.kind == t {
			return c
		}
	}
	return nil
}

func (n *node) checkMethodNotAllowed() HandlerFunc {
	for _, m := range methods {
		if h := n.findHandler(m); h != nil {
			return MethodNotAllowedHandler
		}
	}
	return NotFoundHandler
}
